﻿#include "FITKGraphObjectPostBase.h"

// VTK
#include <vtkProperty.h>
#include <vtkDataSet.h>
#include <vtkMapper.h>
#include <vtkCellData.h>
#include <vtkPointData.h>
#include <vtkDataArray.h>
#include <vtkLookupTable.h>
#include <vtkScalarBarActor.h>
#include <vtkProperty2D.h>
#include <vtkTextProperty.h>
#include <vtkAlgorithmOutput.h>
#include <vtkTextActor.h>
#include <vtkVolume.h>
// #include <vtkLODProp3D.h>
#include <vtkOpenGLProjectedTetrahedraMapper.h>
// #include <vtkUnstructuredGridVolumeRayCastMapper.h>
#include <vtkUnstructuredGridBunykRayCastFunction.h>
#include <vtkColorTransferFunction.h>
#include <vtkVolumeProperty.h>
#include <vtkPiecewiseFunction.h>
#include <vtkDataSetTriangleFilter.h>
#include <vtkOutlineFilter.h>

// Case
#include "FITK_Kernel/FITKCore/FITKAbstractDataObject.h"

// Graph
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITKGraphCommons.h"

#define TEMP_VOL_ARR_NAME "__vol_arr__"

namespace Exchange
{
    FITKGraphObjectPostBase::FITKGraphObjectPostBase(AbaqusData::FITKDataCase* caseData, Core::FITKAbstractDataObject* dataObj)
        : FITKGraphObject3D(caseData, dataObj)
    {
        // Set the layer need to be rendered.
        setRenderLayer(0, 0);

        // Save the post data information.
        m_graphInfo.Type = OtherGraph;
  
#ifdef RENDER_VOLUME_TEST
        // Volume test.
        m_repType = RepType::Volume;
#endif
    }

    FITKGraphObjectPostBase::~FITKGraphObjectPostBase()
    {
#ifdef RENDER_VOLUME_TEST
        // Delete vtk objects.
        deleteVtkObj(m_volFilter);
        deleteVtkObj(m_volOutlineFilter);
        deleteVtkObj(m_volLut);
#endif
    }

    void FITKGraphObjectPostBase::initActors()
    {
#ifdef RENDER_VOLUME_TEST
        // Initialize the look up table for volume.
        m_volLut = vtkLookupTable::New();

        // Initialize the filter for rendering volume.
        m_volFilter = vtkDataSetTriangleFilter::New();

        // Initialize the volume.
        m_volume = vtkVolume::New();

        vtkSmartPointer<vtkOpenGLProjectedTetrahedraMapper> vMapper =
            vtkSmartPointer<vtkOpenGLProjectedTetrahedraMapper>::New();
        vMapper->SetInputConnection(m_volFilter->GetOutputPort());
        vMapper->SetBlendModeToComposite();
        // vMapper->SetAutoAdjustSampleDistances(true);

        //vtkSmartPointer<vtkUnstructuredGridBunykRayCastFunction> rayFunc =
        //    vtkSmartPointer<vtkUnstructuredGridBunykRayCastFunction>::New();
        //vMapper->SetRayCastFunction(rayFunc);

        // Initialize volume properties.
        vtkSmartPointer<vtkVolumeProperty> vProp = vtkSmartPointer<vtkVolumeProperty>::New();
        vProp->ShadeOff();
        vProp->SetInterpolationTypeToLinear();
        vProp->SetIndependentComponents(true);
        vProp->SetColor(vtkSmartPointer<vtkColorTransferFunction>::New());
        
        //vProp->SetAmbient(0.4);
        //vProp->SetDiffuse(0.6);
        //vProp->SetSpecular(0.2);
        //vProp->SetSpecularPower(1);
        
        // Opacity.
        vtkSmartPointer<vtkPiecewiseFunction> vOpa = vtkSmartPointer<vtkPiecewiseFunction>::New();
        // vOpa->SetClamping(true);
        vOpa->AddPoint(0., 0.2);
        vOpa->AddPoint(127.5, 0.4);
        vOpa->AddPoint(255., 0.2);
        vProp->SetScalarOpacity(vOpa);
        //vProp->SetGradientOpacity(vOpa);

        m_volume->SetMapper(vMapper);
        m_volume->SetProperty(vProp);
        m_volume->SetVisibility(true);
        addActor(m_volume);

        // The simplified outline actor and the outline filter.
        m_volSimpleActor = FITKGraphActor::New();
        m_volSimpleActor->setActorType(ActorType::EdgeActor);
        m_volSimpleActor->setGraphObject(this);
        m_volSimpleActor->SetPickable(false);
        m_volSimpleActor->SetVisibility(false);

        m_volOutlineFilter = vtkOutlineFilter::New();
        m_volSimpleActor->setInputConnection(m_volOutlineFilter->GetOutputPort());

        addActor(m_volSimpleActor);
#endif

        // Initialize the actor.
        m_fActor = FITKGraphActor::New();
        m_fActor->SetPickable(true);
        m_fActor->setGraphObject(this);
        m_fActor->SetVisibility(true);
        m_fActor->GetProperty()->SetRepresentation(2);
        m_fActor->GetProperty()->SetEdgeVisibility(true);
        m_fActor->GetProperty()->SetEdgeColor(0., 0., 0.);
        m_fActor->GetProperty()->SetLineWidth(1.);

        // Initialize light properties.
        setLightPropertiesForSurface(m_fActor);

        // Set the mesh color.
        // Old version.
        //@{
        //double colorMesh[3]{ 0., 0., 0. };
        //FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_postMeshColor, colorMesh);
        //m_fActor->GetProperty()->SetColor(colorMesh);
        //@}

        // New version.
        //@{
        setActorColor(m_fActor, FITKGraphCommons::s_postMeshColor);
        //@}

        double colorAboveAndBelow[4]{ 0., 0., 0., 1. };
        FITKGraphCommons::QColorToDouble3(Qt::gray, colorAboveAndBelow);

        // Initialize the lookup table properties.
        vtkLookupTable* lut = getActorLookupTable(m_fActor);
        if (lut)
        {
            lut->SetUseAboveRangeColor(true);
            lut->SetUseBelowRangeColor(true);

            lut->SetAboveRangeColor(colorAboveAndBelow);
            lut->SetBelowRangeColor(colorAboveAndBelow);
        }

#ifndef RENDER_VOLUME_TEST
        addActor(m_fActor);
#endif

        // Initialize the scalar bar for this graph actor.
        //@{
        m_scalarBarActor = vtkScalarBarActor::New();
        m_scalarBarActor->SetVisibility(false);
        m_scalarBarActor->SetTitle("NONE");

        // Set the frame properties.
        // m_scalarBarActor->DrawFrameOn();
        m_scalarBarActor->GetFrameProperty()->SetLineWidth(2.);
        m_scalarBarActor->GetFrameProperty()->SetOpacity(0.7);
        m_scalarBarActor->GetFrameProperty()->SetColor(1., 1., 1.);

        // Set the value format.
        m_scalarBarActor->SetLabelFormat("%-#.3e");

        // Set the lookup table.
        //@{
        vtkSmartPointer<vtkLookupTable> lutBar = vtkSmartPointer<vtkLookupTable>::New();
        lutBar->SetUseAboveRangeColor(true);
        lutBar->SetUseBelowRangeColor(true);
        lutBar->SetAboveRangeColor(colorAboveAndBelow);
        lutBar->SetBelowRangeColor(colorAboveAndBelow);
        m_scalarBarActor->SetLookupTable(lutBar);
        //@}

        // Annotation right.
        // m_scalarBarActor->SetTextPositionToPrecedeScalarBar();

        // Title font
        m_scalarBarActor->GetTitleTextProperty()->SetBold(false);
        m_scalarBarActor->GetTitleTextProperty()->SetItalic(false);
        m_scalarBarActor->GetTitleTextProperty()->SetFontSize(15);
        m_scalarBarActor->GetTitleTextProperty()->SetColor(1., 1., 1.);
        m_scalarBarActor->GetTitleTextProperty()->SetUseTightBoundingBox(true);
        m_scalarBarActor->GetTitleTextProperty()->SetJustificationToLeft();
        m_scalarBarActor->GetTitleTextProperty()->SetVerticalJustificationToTop();

        // Ticker labels font.
        m_scalarBarActor->GetLabelTextProperty()->SetBold(false);
        m_scalarBarActor->GetLabelTextProperty()->SetItalic(false);
        m_scalarBarActor->GetLabelTextProperty()->SetFontSize(14);
        m_scalarBarActor->GetLabelTextProperty()->SetColor(1., 1., 1.);

        m_scalarBarActor->SetUnconstrainedFontSize(true);
        m_scalarBarActor->SetVerticalTitleSeparation(8);
        m_scalarBarActor->SetTitleRatio(1);
        m_scalarBarActor->SetBarRatio(0.2);

        // Annotation text font.
        m_scalarBarActor->GetAnnotationTextProperty()->SetBold(false);
        m_scalarBarActor->GetAnnotationTextProperty()->SetItalic(false);
        m_scalarBarActor->GetAnnotationTextProperty()->SetFontSize(14);
        m_scalarBarActor->GetAnnotationTextProperty()->SetColor(1., 1., 1.);
               
        // Update the scalar bar's properties.
        m_scalarBarActor->Modified();

        addActor(m_scalarBarActor);
        //@}

        // Initialize text actor.
        m_textActor = vtkTextActor::New();
        addActor(m_textActor);

        // Initialize the bar's properties.
        setNumberOfColorStep(m_numColors);
        setNumberOfBarColorStep(m_numColors);
        setNumberOfLabels(m_numColors + 1);

        // Update the bar's size and position.
        updateScalarBarSizeAndPos();
    }

    void FITKGraphObjectPostBase::setNumberOfColorStep(int nSteps)
    {
        // Set the color steps for actor.
        setNumberOfColorStep(m_fActor, nSteps);
    }

    void FITKGraphObjectPostBase::setNumberOfBarColorStep(int nSteps)
    {
        // Set the color steps for bar.
        if (m_scalarBarActor)
        {
            vtkLookupTable* lut = vtkLookupTable::SafeDownCast(m_scalarBarActor->GetLookupTable());
            if (lut)
            {
                lut->SetNumberOfColors(nSteps);
            }
        }
    }

    void FITKGraphObjectPostBase::setEnableDeform(bool isOn)
    {
        // Override in sub-class.
        Q_UNUSED(isOn);
    }

    void FITKGraphObjectPostBase::changeField(int& type, QString& name, QString& compName)
    {
        // Override if necessary.
        if (!m_fActor)
        {
            return;
        }

        // Change the only actor's color array.
        double range[2]{ 0., 0. };
        changeField(m_fActor, type, name, compName, range);

#ifdef RENDER_VOLUME_TEST
        changeField(m_volume, m_volLut, type, name, compName, range);
#endif

        // Save the color mode.
        m_showContour = (type != 0 && !name.isEmpty());

        // Update the scalar bar's visibility.
        updateScalarBarVisible();

        // Change the scalar bar's information.
        QString title = name;
        if (!compName.isEmpty() && name != compName)
        {
            title += (", " + compName);
        }

        updateScalarBarInfo(title, range);
    }

    void FITKGraphObjectPostBase::changeField(int& type, QString& name, int& compIndex, QString& compName)
    {
        // Override if necessary.
        if (!m_fActor)
        {
            return;
        }

        // Change the only actor's color array.
        double range[2]{ 0., 0. };
        changeField(m_fActor, type, name, compIndex, compName, range);

#ifdef RENDER_VOLUME_TEST
        changeField(m_volume, m_volLut, type, name, compIndex, compName, range);
#endif

        // Save the color mode.
        m_showContour = (type != 0 && !name.isEmpty());

        // Update the scalar bar's visibility.
        updateScalarBarVisible();

        // Change the scalar bar's information.( Not support this interface. )
        QString title = name;
        if (!compName.isEmpty() && name != compName)
        {
            title += (", " + compName);
        }

        updateScalarBarInfo(title, range);
    }

    void FITKGraphObjectPostBase::setScalarBarVisibility(bool visibile)
    {
        m_scalarBarVisible = visibile;

        // Update the scalar bar's visibility.
        updateScalarBarVisible();
    }

    void FITKGraphObjectPostBase::setHueType(HueType type)
    {
        // Override if necessary.
        if (!m_fActor)
        {
            return;
        }

        setHueType(m_fActor, type);

#ifdef RENDER_VOLUME_TEST
        setHueType(m_volume, m_volLut, type);
#endif

        setHueType(m_scalarBarActor, type);
    }

    void FITKGraphObjectPostBase::setScalarRange(double min, double max)
    {
        // Override if necessary.
        if (!m_fActor)
        {
            return;
        }

        setScalarRange(m_fActor, min, max);
    }

    void FITKGraphObjectPostBase::setMaxAndMinVisible(bool visibile)
    {
        // Override if neccessary.
        Q_UNUSED(visibile);
    }

    void FITKGraphObjectPostBase::setTransparency(double value)
    {
        if (!m_fActor)
        {
            return;
        }

        m_fActor->GetProperty()->SetOpacity(qMax(qMin(1. - value, 1.), 0.));
    }

    void FITKGraphObjectPostBase::update(bool forceUpdate)
    {
        Q_UNUSED(forceUpdate);

        if (!m_fActor)
        {
            return;
        }

        // Get the mapper and input.
        vtkMapper* mapper = m_fActor->GetMapper();
        if (!mapper)
        {
            return;
        }

        vtkDataSet* dataSet = mapper->GetInputAsDataSet();
        if (!dataSet)
        {
            return;
        }

        // Get the array and component id.
        int arrayId = mapper->GetArrayId();
        int compId = mapper->GetArrayComponent();
        if (arrayId == -1 || compId == -1)
        {
            return;
        }

        // Get the data array.
        vtkDataArray* dArray{ nullptr };
        vtkDataSetAttributes* dAttri{ nullptr };
        int colorMode = mapper->GetScalarMode();
        if (colorMode == VTK_SCALAR_MODE_USE_CELL_FIELD_DATA)
        {
            dAttri = dataSet->GetCellData();
        }
        else if (colorMode == VTK_SCALAR_MODE_USE_POINT_FIELD_DATA)
        {
            dAttri = dataSet->GetPointData();
        }

        if (dAttri)
        {
            dArray = dAttri->GetArray(arrayId);
        }

        if (!dArray)
        {
            return;
        }

        // Set the range to mapper and scalar bar.
        double* dataRange = dArray->GetFiniteRange(compId);
        double delta = dataRange[1] - dataRange[0];
        dataRange[0] -= delta * 0.001;
        dataRange[1] += delta * 0.001;
        mapper->SetScalarRange(dataRange);
        updateScalarBarRange(dataRange);
        updateScalarBarVisible();
    }

    bool FITKGraphObjectPostBase::getFixedBounds(double* bounds)
    {
        // Get the fActor's bounds only.
        if (!m_fActor)
        {
            return false;
        }

        double bds[6] = { 0., 0., 0., 0., 0., 0. };
        bool flag = getActorBounds(m_fActor, bds);
        if (!flag)
        {
            return false;
        }

        for (int i = 0; i < 6; i++)
        {
            bounds[i] = bds[i];
        }

        return true;
    }

    void FITKGraphObjectPostBase::changeField(FITKGraphActor* actor, int& type, QString& name, QString& compName, double* range)
    {
        // Get the index of component.
        int nComp = 0;
        int compIndex = getComponentIndex(actor, type, name, compName, nComp);
        if (nComp == 0 && (type == 1 || type == 2)) // compIndex == -1 || 
        {
            return;
        }

        // Try to change the color array.
        changeField(actor, type, name, compIndex, compName, range);
    }

    void FITKGraphObjectPostBase::changeField(FITKGraphActor* actor, int& type, QString& name, int& compIndex, QString& compName, double* range)
    {
        // Check the input.
        if (!actor)
        {
            return;
        }

        if ((type != 1 && type != 2) || name.isEmpty())
        {
            name.clear();
            type = 0;
            compIndex = 0;
            compName.clear();

            // Old version.
            //@{
            actor->setScalarVisibility(false);
            //@}

            // New version.
            //@{
            setActorColor(actor, FITKGraphCommons::s_postMeshColor);
            //@}

            // Update the actor.
            actor->update();

            return;
        }

        // Get the mapper, and select the array.
        vtkMapper* mapper = actor->GetMapper();
        if (!mapper)
        {
            return;
        }

        vtkDataSet* dataSet = mapper->GetInputAsDataSet();
        if (!dataSet)
        {
            return;
        }

        // Change the color mode.
        vtkDataSetAttributes* dataAttri{ nullptr };
        if (type == 1)
        {
            actor->setScalarVisibility(true);
            actor->setScalarModeToUsePointFieldData();
            dataAttri = dataSet->GetPointData();
        }
        else if (type == 2)
        {
            actor->setScalarVisibility(true);
            actor->setScalarModeToUseCellFieldData();
            dataAttri = dataSet->GetCellData();
        }

        // Change the array by itself.
        // mapper->SelectColorArray(QStringToCharA(name));  

        mapper->ColorByArrayComponent(QStringToCharA(name), compIndex);

        if (dataAttri)
        {
            vtkDataArray* dArray = dataAttri->GetArray(QStringToCharA(name));
            if (dArray)
            {
                double* aRange = dArray->GetFiniteRange(compIndex);
                if (aRange)
                {
                    double delta = aRange[1] - aRange[0];
                    range[0] = aRange[0] - 0.001 * delta;
                    range[1] = aRange[1] + 0.001 * delta;
                }
            }
        }

        // Change the actor to white.
        setActorColor(actor, Qt::white);

        // Set the scalar range.
        mapper->SetScalarRange(range);

        // MUST set the vector component index if the index is not 0.
        vtkLookupTable* lut = vtkLookupTable::SafeDownCast(mapper->GetLookupTable());
        if (lut)
        {
            if (compIndex == -1)
            {
                lut->SetVectorModeToMagnitude();
            }
            else
            {
                lut->SetVectorModeToComponent();
            }

            lut->SetVectorComponent(compIndex);
            lut->SetRange(range);
            lut->Build();
        }

        // Update the actor.
        actor->update();
    }

    void FITKGraphObjectPostBase::changeField(vtkVolume* volume, vtkLookupTable* lut, int& type, QString& name, QString& compName, double* range)
    {
        if (!volume)
        {
            return;
        }

        // Get the index of component.
        int nComp = 0;
        int compIndex = getComponentIndex(volume, type, name, compName, nComp);
        if (nComp == 0 && (type == 1 || type == 2)) // compIndex == -1 || 
        {
            return;
        }

        // Try to change the color array.
        changeField(volume, lut, type, name, compIndex, compName, range);
    }

    void FITKGraphObjectPostBase::changeField(vtkVolume* volume, vtkLookupTable* lut, int& type, QString& name, int& compIndex, QString& compName, double* range)
    {
        // Check the input.
        if (!volume || !lut)
        {
            return;
        }

        if ((type != 1 && type != 2) || name.isEmpty())
        {
            name.clear();
            type = 0;
            compIndex = 0;
            compName.clear();

            return;
        }

        // Get the mapper, and select the array.
        vtkAbstractVolumeMapper* vMapper =
            vtkAbstractVolumeMapper::SafeDownCast(volume->GetMapper());
        if (!vMapper)
        {
            return;
        }

        vtkDataSet* dataSet = vMapper->GetDataSetInput();
        if (!dataSet)
        {
            return;
        }

        // Delete history scalar.
        vtkDataSetAttributes* cellData = dataSet->GetCellData();
        if (cellData)
        {
            vtkDataArray* cellArr = cellData->GetArray(TEMP_VOL_ARR_NAME);
            if (cellArr)
            {
                cellData->RemoveArray(TEMP_VOL_ARR_NAME);
                cellArr->Delete();
            }
        }

        vtkDataSetAttributes* pointData = dataSet->GetPointData();
        if (pointData)
        {
            vtkDataArray* ptArr = pointData->GetArray(TEMP_VOL_ARR_NAME);
            if (ptArr)
            {
                pointData->RemoveArray(TEMP_VOL_ARR_NAME);
                ptArr->Delete();
            }
        }

        // Change the color mode.
        vtkDataSetAttributes* dataAttri{ nullptr };
        if (type == 1)
        {
            vMapper->SetScalarModeToUsePointFieldData();
            dataAttri = dataSet->GetPointData();
        }
        else if (type == 2)
        {
            vMapper->SetScalarModeToUseCellFieldData();
            dataAttri = dataSet->GetCellData();
        }

        if (!dataAttri)
        {
            return;
        }

        vtkDataArray* dArray = dataAttri->GetArray(QStringToCharA(name));
        if (!dArray)
        {
            return;
        }

        double* aRange = dArray->GetFiniteRange(compIndex);
        if (aRange)
        {
            double delta = aRange[1] - aRange[0];
            range[0] = aRange[0] - 0.001 * delta;
            range[1] = aRange[1] + 0.001 * delta;
        }

        if (compIndex == -1)
        {
            // Change the array by itself.
            vMapper->SelectScalarArray(QStringToCharA(name));
        }
        else
        {
            // Create a new array for field component.
            vtkDataArray* newArray = vtkDataArray::CreateDataArray(dArray->GetDataType());
            newArray->SetName(TEMP_VOL_ARR_NAME);
            newArray->SetNumberOfComponents(1);
            dataAttri->AddArray(newArray);

            // Copy data from field data.
            newArray->SetNumberOfValues(dArray->GetNumberOfTuples());
            newArray->CopyComponent(0, dArray, compIndex);

            // Change the array by new array component.
            vMapper->SelectScalarArray(TEMP_VOL_ARR_NAME);
        }

        // Update the look up table range.
        vtkPiecewiseFunction* gpo = volume->GetProperty()->GetGradientOpacity();
        vtkPiecewiseFunction* spo = volume->GetProperty()->GetScalarOpacity();
        vtkColorTransferFunction* colors = volume->GetProperty()->GetRGBTransferFunction();
        if (gpo)
        {
            gpo->AdjustRange(range);  
        }

        if (spo)
        {
            spo->AdjustRange(range);
        }

        // Initialize colors with the data range.
        if (colors)
        {
            colors->RemoveAllPoints();

            int nColors = lut->GetNumberOfAvailableColors();
            double rgba[4];
            double delta = (range[1] - range[0]) / (nColors - 1);

            for (int i = 0; i < nColors; i++)
            {
                lut->GetIndexedColor(i, rgba);
                colors->AddRGBPoint(range[0] + delta * i, rgba[0], rgba[1], rgba[2]);
            }
        }

        // Save the range to the look up table.
        lut->SetRange(range);

        //if (compIndex == -1)
        //{
        //    vMapper->SetVectorMode(vtkSmartVolumeMapper::VectorModeType::MAGNITUDE);            
        //}
        //else
        //{
        //    vMapper->SetVectorMode(vtkSmartVolumeMapper::VectorModeType::COMPONENT);
        //    vMapper->SetVectorComponent(compIndex);
        //} 

        // Update the volume.
        volume->Update();

        //if (compIndex != -1)
        //{
        //    // Remove and delete the array.
        //    vtkDataArray* newArray = dataAttri->GetArray(TEMP_VOL_ARR_NAME);
        //    if (newArray)
        //    {
        //        dataAttri->RemoveArray(TEMP_VOL_ARR_NAME);
        //        newArray->Delete();
        //    }
        //}
    }

    vtkDataSet* FITKGraphObjectPostBase::getActorDataSet(FITKGraphActor* actor)
    {
        // Check the actor.
        if (!actor)
        {
            return nullptr;
        }

        // Check the data mapper.
        vtkMapper* mapper = actor->GetMapper();
        if (!mapper)
        {
            return nullptr;
        }

        // Try to get the input data.
        vtkDataSet* dataSet = mapper->GetInputAsDataSet();
        if (dataSet)
        {
            return dataSet;
        }

        //// Try to get the input connection.
        //vtkAlgorithm* inputAl = mapper->GetInputAlgorithm();
        //if (!inputAl)
        //{
        //    return nullptr;
        //}

        //dataSet = vtkDataSet::SafeDownCast(inputAl->GetOutputDataObject(0));
        //if (dataSet)
        //{
        //    return dataSet;
        //}

        return nullptr;
    }

    vtkLookupTable* FITKGraphObjectPostBase::getActorLookupTable(FITKGraphActor* actor)
    {
        if (!actor)
        {
            return nullptr;
        }

        // Check the mapper.
        vtkMapper* mapper = actor->GetMapper();
        if (!mapper)
        {
            return nullptr;
        }

        // Get the lookup table.
        vtkLookupTable* lut = vtkLookupTable::SafeDownCast(mapper->GetLookupTable());
        return lut;
    }

    int FITKGraphObjectPostBase::getComponentIndex(FITKGraphActor* actor, int type, QString name, QString compName, int& nComp)
    {
        // Return -1 if the component or array does not exist.
        int index = -1;
        nComp = 0;

        // Check the mesh data.
        vtkDataSet* dataSet = getActorDataSet(actor);
        if (!dataSet)
        {
            return index;
        }

        return getComponentIndex(dataSet, type, name, compName, nComp);
    }

    int FITKGraphObjectPostBase::getComponentIndex(vtkVolume* volume, int type, QString name, QString compName, int& nComp)
    {
        // Return -1 if the component or array does not exist.
        int index = -1;
        nComp = 0;

        if (!volume)
        {
            return index;
        }

        // Check the mesh data.
        vtkAbstractVolumeMapper* vMapper = volume->GetMapper();
        if (!vMapper)
        {
            return index;
        }
        
        vtkDataSet* dataSet = vMapper->GetDataSetInput();
        if (!dataSet)
        {
            return index;
        }

        return getComponentIndex(dataSet, type, name, compName, nComp);
    }

    int FITKGraphObjectPostBase::getComponentIndex(vtkDataSet* dataSet, int type, QString name, QString compName, int& nComp)
    {
        // Return -1 if the component or array does not exist.
        int index = -1;
        nComp = 0;

        if (!dataSet)
        {
            return index;
        }

        // The cell or point data.
        vtkDataSetAttributes* data{ nullptr };
        if (type == 1)
        {
            data = dataSet->GetPointData();
        }
        else if (type == 2)
        {
            data = dataSet->GetCellData();
        }

        if (!data)
        {
            return index;
        }

        // Check the array and find the component.
        int aIndex = -1;
        int nArray = data->GetNumberOfArrays();

        vtkDataArray* dArray{ nullptr };
        for (int i = 0; i < nArray; i++)
        {
            QString arrName = data->GetArrayName(i);
            if (arrName == name)
            {
                aIndex = i;
                dArray = data->GetArray(i);
                break;
            }
        }

        if (!dArray)
        {
            return index;
        }

        // Get the number of components.
        nComp = dArray->GetNumberOfComponents();

        if (nComp > 1)
        {
            // Check if the component is the mag.
            if (compName == "Magnitude")
            {
                return -1;
            }

            for (int i = 0; i < nComp; i++)
            {
                QString cName = dArray->GetComponentName(i);
                if (compName == cName)
                {
                    index = i;
                    return index;
                }
            }
        }

        return 0;
    }

    void FITKGraphObjectPostBase::setNumberOfLabels(int nLabels)
    {
        if (!m_scalarBarActor || nLabels <= 0)
        {
            return;
        }

        m_scalarBarActor->SetNumberOfLabels(qMin(nLabels, c_maxLabelAndColorNumber));
        m_scalarBarActor->Modified();

        // Update the scalar bar.
        updateScalarBarSizeAndPos();
    }

    void FITKGraphObjectPostBase::setNumberOfColorStep(FITKGraphActor* actor, int nSteps)
    {
        if (!actor || nSteps <= 0)
        {
            return;
        }

        // Check the mapper and lookup table.
        vtkLookupTable* lut = getActorLookupTable(actor);
        vtkMapper* mapper = actor->GetMapper();
        if (!lut || !mapper)
        {
            return;
        }

        // Set the color interpolation method.
        bool isGradient = (nSteps == 255);
        mapper->SetInterpolateScalarsBeforeMapping(!isGradient);

        if (!isGradient)
        {
            // Set the color number of the look up table.
            lut->SetNumberOfTableValues(qMin(nSteps, c_maxLabelAndColorNumber));
            lut->Build();
        }

        // Update the actor.
        actor->Modified();

        // Update the scalar bar.
        updateScalarBarSizeAndPos();
    }

    void FITKGraphObjectPostBase::setColorDataRange(FITKGraphActor* actor, double min, double max)
    {
        vtkLookupTable* lut = getActorLookupTable(actor);
        if (!lut)
        {
            return;
        }

        // Rebuild the lookup table.
        lut->SetRange(min, max);
        lut->Build();

        // Update the actor.
        actor->Modified();
    }

    void FITKGraphObjectPostBase::setHueType(FITKGraphActor* actor, HueType type)
    {
        if (!actor)
        {
            return;
        }

        // Get the lookup table from the actor.
        vtkLookupTable* lut = getActorLookupTable(actor);
        setHueType(lut, type);
        actor->update();
    }

    void FITKGraphObjectPostBase::setHueType(vtkVolume* volume, vtkLookupTable* lut, HueType type)
    {
        if (!volume || !lut)
        {
            return;
        }

        // Save the range first.
        double* r = lut->GetRange();
        double range[2]{ r[0], r[1] };

        // Set the HSV for double colors.
        lut->DeepCopy(vtkSmartPointer<vtkLookupTable>::New());
        lut->SetNumberOfColors(m_numColors);

        switch (type)
        {
        case BlackToWhite:
        {
            lut->SetHueRange(-1., -1.);
            lut->SetSaturationRange(0., 0.);
            lut->SetValueRange(0., 1.);
            break;
        }
        case Rainbow:
        {
            lut->SetHueRange(0.666667, 0.);
            lut->SetSaturationRange(1., 1.);
            lut->SetValueRange(1., 1.);
            break;
        }
        case ReversedRainbow:
        {
            lut->SetHueRange(0., 0.666667);
            lut->SetSaturationRange(1., 1.);
            lut->SetValueRange(1., 1.);
            break;
        }
        case WhiteToBlack:
        {
            lut->SetHueRange(-1., -1.);
            lut->SetSaturationRange(0., 0.);
            lut->SetValueRange(1., 0.);
            break;
        }
        default:
            return;
        }

        lut->Build();

        // Initialize volume colors.
        //@{
        vtkColorTransferFunction* colors = volume->GetProperty()->GetRGBTransferFunction();
        if (!colors)
        {
            volume->GetProperty()->SetColor(vtkSmartPointer<vtkColorTransferFunction>::New());
            colors = volume->GetProperty()->GetRGBTransferFunction();
        }

        // Clear data.
        colors->RemoveAllPoints();

        int nColors = lut->GetNumberOfAvailableColors();       
        double rgba[4];
        double delta = (range[1] - range[0]) / (nColors - 1);

        for (int i = 0; i < nColors; i++)
        {
            lut->GetIndexedColor(i, rgba);
            colors->AddRGBPoint(range[0] + delta * i, rgba[0], rgba[1], rgba[2]);
        }

        volume->Update();
        //@}
    }

    void FITKGraphObjectPostBase::setHueType(vtkScalarBarActor* actor, HueType type)
    {
        if (!actor)
        {
            return;
        }

        vtkLookupTable* lut = vtkLookupTable::SafeDownCast(actor->GetLookupTable());
        setHueType(lut, type);
        actor->Modified();
    }

    void FITKGraphObjectPostBase::setHueType(vtkLookupTable* lut, HueType type)
    {
        if (!lut)
        {
            return;
        }

        // Set the HSV for double colors.
        switch (type)
        {
        case BlackToWhite:
        {
            lut->SetHueRange(-1., -1.);
            lut->SetSaturationRange(0., 0.);
            lut->SetValueRange(0., 1.);
            break;
        }
        //case BlueToRed:
        //{
        //    lut->SetHueRange(0.666667, 0.);
        //    lut->SetSaturationRange(1., 1.);
        //    lut->SetValueRange(1., 1.);
        //    break;
        //}
        case Rainbow:
        {
            lut->SetHueRange(0.666667, 0.);
            lut->SetSaturationRange(1., 1.);
            lut->SetValueRange(1., 1.);
            break;
        }
        //case RedToBlue:
        //{
        //    lut->SetHueRange(0., 0.666667);
        //    lut->SetSaturationRange(1., 1.);
        //    lut->SetValueRange(1., 1.);
        //    break;
        //}
        case ReversedRainbow:
        {
            lut->SetHueRange(0., 0.666667);
            lut->SetSaturationRange(1., 1.);
            lut->SetValueRange(1., 1.);
            break;
        }
        case WhiteToBlack:
        {
            lut->SetHueRange(-1., -1.);
            lut->SetSaturationRange(0., 0.);
            lut->SetValueRange(1., 0.);
            break;
        }
        default:
            return;
        }

        // Rebuild the lookup table and update the actor.
        lut->Build();
    }

    void FITKGraphObjectPostBase::setScalarRange(FITKGraphActor* actor, double min, double max)
    {
        if (!actor)
        {
            return;
        }

        // Get the mapper and set the scalar range.
        vtkMapper* mapper = actor->GetMapper();
        if (!mapper)
        {
            return;
        }

        mapper->SetScalarRange(min, max);
        mapper->Modified();

        // Update the scalar bar range.
        updateScalarBarRange(min, max);
    }

    void FITKGraphObjectPostBase::setActorColor(FITKGraphActor* actor, QColor color)
    {
        if (!actor || !color.isValid())
        {
            return;
        }

        double color3[3]{ 0., 0., 0. };
        FITKGraphCommons::QColorToDouble3(color, color3);
        actor->GetProperty()->SetColor(color3);

        //// Get the data set and get the number of cells.
        //vtkDataSet* dataSet = getActorDataSet(actor);
        //if (!dataSet)
        //{
        //    return;
        //}

        //// Use the scalar to color the actor.
        //vtkCellData* cellData = dataSet->GetCellData();
        //if (!cellData)
        //{
        //    return;
        //}

        //int nCells = dataSet->GetNumberOfCells();
        //double color3[3]{ 0., 0., 0. };
        //FITKGraphCommons::QColorToDouble3(color, color3);

        //// Create the color array.
        //vtkSmartPointer<vtkUnsignedCharArray> colorArray = vtkSmartPointer<vtkUnsignedCharArray>::New();
        //colorArray->SetNumberOfComponents(3);
        //colorArray->SetNumberOfTuples(nCells);

        //// Fill color array.
        //colorArray->FillComponent(0, color3[0]);
        //colorArray->FillComponent(1, color3[1]);
        //colorArray->FillComponent(2, color3[2]);

        //// Set the color array to model.
        //cellData->SetScalars(colorArray);

        //actor->setScalarModeToUseCellData();
    }

    void FITKGraphObjectPostBase::updateScalarBarSizeAndPos()
    {
        if (!m_scalarBarActor)
        {
            return;
        }

        int nColors = 0;
        int nLabels = m_scalarBarActor->GetNumberOfLabels();
        vtkLookupTable* lutBar = vtkLookupTable::SafeDownCast(m_scalarBarActor->GetLookupTable());
        if (lutBar)
        {
            nColors = lutBar->GetNumberOfColors();
        }

        // The max count is 25 for now.
        double height = 0.05 + qMin(qMax(nLabels, nColors), c_maxLabelAndColorNumber) * 0.03;

        // Add to renderer, set fix size and postion ratio
        m_scalarBarActor->GetPositionCoordinate()->SetCoordinateSystemToNormalizedDisplay();
        m_scalarBarActor->SetPosition(0.05, 0.95 - height); // Position

        // 0.03 per label or color ( contains title )
        m_scalarBarActor->SetPosition2(0.1, height); // Size
    }

    void FITKGraphObjectPostBase::updateScalarBarVisible()
    {
        if (!m_scalarBarActor || !m_fActor)
        {
            return;
        }

        if (m_showContour)
        {
            m_scalarBarActor->SetVisibility(m_scalarBarVisible && m_fActor->GetVisibility());
        }
        else
        {
            m_scalarBarActor->SetVisibility(false);
        }
    }

    void FITKGraphObjectPostBase::updateScalarBarInfo(QString title, double* range)
    {
        if (!m_scalarBarActor || !range)
        {
            return;
        }

        // Deal the title name.
        //if (title.startsWith("_Magnitude_"))
        //{
        //    title = QString("%1, %2").arg(title.remove("_Magnitude_")).arg("Magnitude");
        //}

        // Set the title.
        m_scalarBarActor->SetTitle(QStringToCharA(title));

        vtkLookupTable* lutBar = vtkLookupTable::SafeDownCast(m_scalarBarActor->GetLookupTable());
        if (lutBar)
        {
            lutBar->SetTableRange(range);
            lutBar->Build();
        }

        m_scalarBarActor->Modified();
    }

    void FITKGraphObjectPostBase::updateScalarBarRange(double* range)
    {
        if (!m_scalarBarActor || !range)
        {
            return;
        }

        // Set th range to lookup table.
        vtkLookupTable* lutBar = vtkLookupTable::SafeDownCast(m_scalarBarActor->GetLookupTable());
        if (lutBar)
        {
            lutBar->SetTableRange(range);
            lutBar->Build();
        }

        m_scalarBarActor->Modified();
    }

    void FITKGraphObjectPostBase::updateScalarBarRange(double min, double max)
    {
        if (!m_scalarBarActor)
        {
            return;
        }

        // Set th range to lookup table.
        vtkLookupTable* lutBar = vtkLookupTable::SafeDownCast(m_scalarBarActor->GetLookupTable());
        if (lutBar)
        {
            lutBar->SetTableRange(min, max);
            lutBar->Build();
        }

        m_scalarBarActor->Modified();
    }

    void FITKGraphObjectPostBase::simplified(bool enabled)
    {
        if (m_graphSimplified)
        {
            // Show or hide the volume actor.
            switch (m_repType)
            {
            case Actor:
                break;
            case Volume:
            {
#ifdef RENDER_VOLUME_TEST
                m_volume->SetVisibility(!enabled && getDataVisibility());
                m_volSimpleActor->SetVisibility(enabled && getDataVisibility());
#endif
                break;
            }
            default:
                return;
            }
        }
    }
}   // namespace Exchange